package org.zh.sh.util.create;

import org.zh.sh.util.ComponentScan;
import org.zh.sh.util.proxy.Service;
import org.zh.sh.util.jdbc.Transaction;

import java.io.BufferedWriter;
import java.io.IOException;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

public class ProxyCreate {

    public static void createProxyService(Class<?> application) throws IOException, ClassNotFoundException {
        Set<String> set = new HashSet<>();
        for (Method method : Object.class.getMethods()) {
            set.add(method.getName());
        }
        List<Class<?>> run = ComponentScan.componentScan(application);
        for (Class<?> clazz : run) {
            Service service = clazz.getAnnotation(Service.class);
            if (service != null) {
                String dir = System.getProperty("user.dir");
                Path staticPath = Paths.get(dir, "clazz", "static.text");
                Files.createDirectories(staticPath.getParent());
                BufferedWriter staticBW = Files.newBufferedWriter(staticPath, StandardOpenOption.APPEND, StandardOpenOption.CREATE, StandardOpenOption.WRITE);
                staticBW.write(" public static final Proxy" + clazz.getSimpleName() + " proxy" + clazz.getSimpleName() + " = new Proxy" + clazz.getSimpleName() + "();\n");
                staticBW.flush();
                staticBW.close();


                String className = "Proxy" + clazz.getSimpleName();
                Path path = Paths.get(dir, "clazz/proxy", className + ".java");
                Files.deleteIfExists(path);
                Files.createDirectories(path.getParent());
                BufferedWriter bw = Files.newBufferedWriter(path, StandardOpenOption.CREATE, StandardOpenOption.WRITE);
                String proxyClass = "import java.sql.SQLException;\n" +
                                    "import java.util.Map;\n" +
                                    "import static org.zh.sh.config.GlobalStatic.removeLocalConn;\n" +
                                    "import static org.zh.sh.config.GlobalStatic.setLocalConn;\n" +
                                    "\n" +
                                    "public class " + className + " extends " + clazz.getSimpleName() + " {\n";
                Method[] methods = clazz.getMethods();
                for (Method method : methods) {
                    if (!set.contains(method.getName())) {
                        proxyClass += "@Override\n" +
                                      "    public " + method.getGenericReturnType().getTypeName() + " " + method.getName() + " ( " + params(method) + " ) " + exceptionTypes(method) + " {\n"
                                      //方法体开始
                                      + "int methodHashCode = " + method.hashCode() + ";\n";
                        if (method.getAnnotation(Transaction.class) != null) {
                            proxyClass += "  setLocalConn(methodHashCode);\n";
                        }
                        if (!method.getGenericReturnType().getTypeName().equals("void")) {
                            proxyClass += "        " + method.getGenericReturnType().getTypeName() + " result = ";
                        }
                        proxyClass += "super." + method.getName() + "(" + params2(method) + ");\n";  //业务方法执行行
                        if (method.getAnnotation(Transaction.class) != null) {
                            proxyClass += " removeLocalConn(methodHashCode);\n";
                        }
                        if (!method.getGenericReturnType().getTypeName().equals("void")) {
                            proxyClass += "        return result;\n";
                        }
                        proxyClass += "    }";
                    }
                }
                proxyClass += "\n}";
                bw.write(proxyClass);
                bw.flush();
                bw.close();
            }
        }
    }

    private static String exceptionTypes(Method method) {
        StringBuilder excep = new StringBuilder();
        Class<?>[] exceptionTypes = method.getExceptionTypes();
        if (exceptionTypes.length > 0) {
            excep.append(" throws ");
            int i = 1;
            for (Class<?> clazz1 : exceptionTypes) {
                if (i == exceptionTypes.length) {
                    excep.append(clazz1.getSimpleName());
                } else {
                    excep.append(clazz1.getSimpleName()).append(" ,");
                }
                i++;
            }
        }
        return excep.toString();
    }

    private static String params2(Method method) {
        StringBuilder params2 = new StringBuilder();
        Parameter[] parameters = method.getParameters();
        if (parameters.length > 0) {
            int i = 1;
            for (Parameter para : parameters) {
                if (i == parameters.length) {
                    params2.append(para.getName());
                } else {
                    params2.append(para.getName()).append(" ,");
                }
                i++;
            }
        }
        return params2.toString();
    }

    private static String params(Method method) {
        StringBuilder str = new StringBuilder();
        Parameter[] parameters = method.getParameters();
        int i = 1;
        for (Parameter para : parameters) {
            if (i == parameters.length) {
                str.append(para.getParameterizedType().getTypeName()).append(" ").append(para.getName());
            } else {
                str.append(para.getParameterizedType().getTypeName()).append(" ").append(para.getName()).append(", ");
            }
            i++;
        }
        return str.toString();
    }



}
